#include "RedisMgr.h"
#include <queue>
#include "ConfigMgr.h"


//这个连接池会被破坏是，所以如果要支持多线程，必须要所还有条件变量的支持
class RedisConPool {
public:
	RedisConPool(size_t poolSize, const char* host, int port, const char* pwd)
		: poolSize_(poolSize), host_(host), port_(port), b_stop_(false) {
		for (size_t i = 0; i < poolSize_; ++i) {
			auto* context = redisConnect(host, port);
			if (context == nullptr || context->err != 0) {
				if (context != nullptr) {
					redisFree(context);
				}
				continue;
			}

			auto reply = (redisReply*)redisCommand(context, "AUTH %s", pwd);
			if (reply->type == REDIS_REPLY_ERROR) {
				std::cout << "认证失败" << std::endl;
				//执行成功 释放redisCommand执行后返回的redisReply所占用的内存
				freeReplyObject(reply);
				continue;
			}

			//执行成功 释放redisCommand执行后返回的redisReply所占用的内存
			freeReplyObject(reply);
			std::cout << "认证成功" << std::endl;
			connections_.push(context);
		}

	}

	~RedisConPool() {
		std::lock_guard<std::mutex> lock(mutex_);
		while (!connections_.empty()) {
			//这里应该把所有的资源释放掉,防止内存泄露问题
			auto it = connections_.front();
			redisFree(it);
			connections_.pop();
		}
	}


	//条件变量是有信号会重新激活这一段代码，重新尝试运行并获取锁，如果没有获取资源，又会解开锁，知道下一次通知
	//如果本身就是满足条件变量，那么就不会在这里挂起，也就不会出现阻塞，并不是每一次运行到条件变量那里就要一个
	//通知的，田间变量的意义在于：如果发生条件不满足，就挂起等待其他人的通知，同时放开锁，如果一开始条件就满足
	//就不会在这里挂起，也就是说，其实，田间变量要求的是一个条件，只要条件满足，就不会阻塞
	redisContext* getConnection() {
		std::unique_lock<std::mutex> lock(mutex_);
		cond_.wait(lock, [this] {
			if (b_stop_) {
				return true;
			}
			return !connections_.empty();
			});
		//如果停止则直接返回空指针
		if (b_stop_) {
			return  nullptr;
		}
		auto* context = connections_.front();
		connections_.pop();
		return context;
	}

	void returnConnection(redisContext* context) {
		std::lock_guard<std::mutex> lock(mutex_);

		//多线程状态机所有的执行都是不确定性，你要考虑到所有有可能发生事件会造成的不同的影响，从而做出应对
		//这里，即使是进入到返回连接，也偶可能此时是已经stop状态
		//如果是stop状态，那么要不要返回呢？
		if (b_stop_) {
			return;
		}

		//连接是可以共享的，所以不用move，当然你也还是可以使用Move
		connections_.push(context);
		cond_.notify_one();
	}

	void Close() {
		b_stop_ = true;
		cond_.notify_all();
	}

private:
	atomic<bool> b_stop_;		//停止标志						
	size_t poolSize_;			//池子大小
	const char* host_;			//redis地址
	int port_;					//端口
	std::queue<redisContext*> connections_;		//所有连接的指针
	std::mutex mutex_;			//锁，保护
	std::condition_variable cond_;	//条件变量，消费者-生产者模型
};


RedisMgr::~RedisMgr()
{

}

bool RedisMgr::Connect(const std::string& host, int port)
{
	//conn = redisConnect(host.c_str(), port);
	//if(_connect!=NULL&&_connect->err)
	//{
	//	std::cout << "connect error " << conn->errstr << std::endl;
	//	return false;
	//}
	return true;
}

bool RedisMgr::Get(const std::string& key, std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "GET %s", key.c_str());
	if (this->_reply == nullptr) {
		std::cout << "[ GET  " << key << " ] failed" << std::endl;
		freeReplyObject(this->_reply);

		_con_pool->returnConnection(conn);
		return false;
	}

	if (this->_reply->type != REDIS_REPLY_STRING) {
		std::cout << "[ GET  " << key << " ] failed" << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	value = this->_reply->str;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	std::cout << "Succeed to execute command [ GET " << key << "  ]" << std::endl;
	return true;

}

bool RedisMgr::Set(const std::string& key, const std::string& value)
{
	//执行redis命令行
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "SET %s %s", key.c_str(), value.c_str());

	//如果返回NULL则说明执行失败
	if (NULL == this->_reply)
	{
		std::cout << "Execut command [ SET " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	//如果执行失败则释放连接
	if (!(this->_reply->type == REDIS_REPLY_STATUS && (strcmp(this->_reply->str, "OK") == 0 || strcmp(this->_reply->str, "ok") == 0)))
	{
		std::cout << "Execut command [ SET " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	//执行成功 释放redisCommand执行后返回的redisReply所占用的内存
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	std::cout << "Execut command [ SET " << key << "  " << value << " ] success ! " << std::endl;
	return true;
}

bool RedisMgr::Auth(const std::string& password)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "AUTH %s", password.c_str());
	if (this->_reply->type == REDIS_REPLY_ERROR) {
		std::cout << "认证失败" << std::endl;
		//执行成功 释放redisCommand执行后返回的redisReply所占用的内存
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	else {
		//执行成功 释放redisCommand执行后返回的redisReply所占用的内存
		freeReplyObject(this->_reply);
		std::cout << "认证成功" << std::endl;
		_con_pool->returnConnection(conn);
		return true;
	}
}

bool RedisMgr::LPush(const std::string& key, const std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "LPUSH %s %s", key.c_str(), value.c_str());
	if (NULL == this->_reply)
	{
		std::cout << "Execut command [ LPUSH " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	if (this->_reply->type != REDIS_REPLY_INTEGER || this->_reply->integer <= 0) {
		std::cout << "Execut command [ LPUSH " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	std::cout << "Execut command [ LPUSH " << key << "  " << value << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

bool RedisMgr::LPop(const std::string& key, std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "LPOP %s ", key.c_str());
	if (_reply == nullptr || _reply->type == REDIS_REPLY_NIL) {
		std::cout << "Execut command [ LPOP " << key << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	value = _reply->str;
	std::cout << "Execut command [ LPOP " << key << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

bool RedisMgr::RPush(const std::string& key, const std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "RPUSH %s %s", key.c_str(), value.c_str());
	if (NULL == this->_reply)
	{
		std::cout << "Execut command [ RPUSH " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	if (this->_reply->type != REDIS_REPLY_INTEGER || this->_reply->integer <= 0) {
		std::cout << "Execut command [ RPUSH " << key << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}

	std::cout << "Execut command [ RPUSH " << key << "  " << value << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

bool RedisMgr::RPop(const std::string& key, std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "RPOP %s ", key.c_str());
	if (_reply == nullptr || _reply->type == REDIS_REPLY_NIL) {
		std::cout << "Execut command [ RPOP " << key << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	value = _reply->str;
	std::cout << "Execut command [ RPOP " << key << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;

}

bool RedisMgr::HSet(const std::string& key, const std::string& hkey, const std::string& value)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "HSET %s %s %s", key.c_str(), hkey.c_str(), value.c_str());
	if (_reply == nullptr || _reply->type != REDIS_REPLY_INTEGER) {
		std::cout << "Execut command [ HSet " << key << "  " << hkey << "  " << value << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	std::cout << "Execut command [ HSet " << key << "  " << hkey << "  " << value << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

bool RedisMgr::HSet(const char* key, const char* hkey, const char* hvalue, size_t hvaluelen)
{
	const char* argv[4];
	size_t argvlen[4];
	argv[0] = "HSET";
	argvlen[0] = 4;
	argv[1] = key;
	argvlen[1] = strlen(key);
	argv[2] = hkey;
	argvlen[2] = strlen(hkey);
	argv[3] = hvalue;
	argvlen[3] = hvaluelen;
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommandArgv(conn, 4, argv, argvlen);
	if (_reply == nullptr || _reply->type != REDIS_REPLY_INTEGER) {
		std::cout << "Execut command [ HSet " << key << "  " << hkey << "  " << hvalue << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	std::cout << "Execut command [ HSet " << key << "  " << hkey << "  " << hvalue << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

std::string RedisMgr::HGet(const std::string& key, const std::string& hkey)
{
	const char* argv[3];
	size_t argvlen[3];
	argv[0] = "HGET";
	argvlen[0] = 4;
	argv[1] = key.c_str();
	argvlen[1] = key.length();
	argv[2] = hkey.c_str();
	argvlen[2] = hkey.length();
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommandArgv(conn, 3, argv, argvlen);
	if (this->_reply == nullptr || this->_reply->type == REDIS_REPLY_NIL) {
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		std::cout << "Execut command [ HGet " << key << " " << hkey << "  ] failure ! " << std::endl;
		return "";
	}

	std::string value = this->_reply->str;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	std::cout << "Execut command [ HGet " << key << " " << hkey << " ] success ! " << std::endl;
	return value;
}

bool RedisMgr::Del(const std::string& key)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "DEL %s", key.c_str());
	if (this->_reply == nullptr || this->_reply->type != REDIS_REPLY_INTEGER) {
		std::cout << "Execut command [ Del " << key << " ] failure ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	std::cout << "Execut command [ Del " << key << " ] success ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

bool RedisMgr::ExistsKey(const std::string& key)
{
	auto conn = _con_pool->getConnection();
	this->_reply = (redisReply*)redisCommand(conn, "exists %s", key.c_str());
	if (this->_reply == nullptr || this->_reply->type != REDIS_REPLY_INTEGER || this->_reply->integer == 0) {
		std::cout << "Not Found [ Key " << key << " ]  ! " << std::endl;
		freeReplyObject(this->_reply);
		_con_pool->returnConnection(conn);
		return false;
	}
	std::cout << " Found [ Key " << key << " ] exists ! " << std::endl;
	freeReplyObject(this->_reply);
	_con_pool->returnConnection(conn);
	return true;
}

void RedisMgr::Close()
{
	_con_pool->Close();
}

RedisMgr::RedisMgr():_reply(nullptr)
{
	auto& gCfgMgr = ConfigMgr::Inst();
	auto host = gCfgMgr["Redis"]["Host"];
	auto port = gCfgMgr["Redis"]["Port"];
	auto pwd = gCfgMgr["Redis"]["Passwd"];
	_con_pool.reset(new RedisConPool(5, host.c_str(), atoi(port.c_str()), pwd.c_str()));
}

